﻿using System;
using System.Collections.Concurrent;
using System.IO;
using System.Linq;
using System.Net.Http;
using System.Threading.Tasks;

using Microsoft.Extensions.Caching.Memory;
using Microsoft.Extensions.DependencyInjection;

using Utils.WeChat.Options;
using Utils.WeChat.Result;

namespace Utils.WeChat
{
    public class WeChatMiniProgram : IDisposable
    {
        private readonly static ConcurrentDictionary<string, WeChatMiniProgram> wechats = new ConcurrentDictionary<string, WeChatMiniProgram>();

        private readonly WeChatAppOptions options;
        private readonly IMemoryCache memoryCache;

        private readonly HttpClient httpClient;

        public WeChatMiniProgram(WeChatAppOptions options, IMemoryCache memoryCache)
        {
            this.options = options;
            this.memoryCache = memoryCache;
            httpClient = new HttpClient();
        }

        /// <summary>
        /// 
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="content"></param>
        /// <returns></returns>
        private async Task<T> ChangeResult<T>(HttpResponseMessage content)
        {
            if (!content.IsSuccessStatusCode)
            {
                throw Error.SystemError("微信小程序接口请求失败");
            }
            string str = await content.Content.ReadAsStringAsync();
            if (string.IsNullOrEmpty(str))
            {
                throw Error.SystemError("微信小程序接口请求失败");
            }
            var result = str.JSONDeserialize<ResultBase>();
            if (result.errcode != 0)
            {
                throw Error.SystemError(result.errmsg);
            }
            return str.JSONDeserialize<T>();
        }


        internal static WeChatMiniProgram GetInstance(IServiceProvider serviceProvider, WeChatAppOptions options)
        {
            return wechats.GetOrAdd(options.AppID, key => ActivatorUtilities.CreateInstance<WeChatMiniProgram>(serviceProvider, options));
        }

        /// <summary>
        /// 微信登录
        /// </summary>
        /// <param name="code"></param>
        /// <returns></returns>
        public Task<ResultMiniProgram> MiniProgramLogin(string code)
        {
            if (code.IsNullOrEmpty()) throw Error.SystemError("code not be null");
            string url = $"https://api.weixin.qq.com/sns/jscode2session?appid={options.AppID}&secret={options.AppSecret}&js_code={code}&grant_type=authorization_code";
            return httpClient.GetAsync(url).JsonResultAsync(ChangeResult<ResultMiniProgram>);
            // return request.GetResultAsync<ResultMiniProgram>();
        }


        /// <summary>
        /// 发送订阅消息
        /// </summary>
        /// <param name="openId"></param>
        /// <param name="templateId"></param>
        /// <param name="data"></param>
        /// <param name="page"></param>
        /// <returns></returns>
        public async Task SendSubscribeMessage(string openId, string templateCode, string page, object data)
        {
            if (!options.TemplateCode.ContainsKey(templateCode)) return;
            string templateId = options.TemplateCode[templateCode];
            string url = "https://api.weixin.qq.com/cgi-bin/message/subscribe/send";
            AppendToken(ref url);
            await httpClient.PostAsync(url, new JsonContent(new
            {
                touser = openId,
                template_id = templateId,
                page = page,
                data = data.ToDictionary().ToDictionary(a => a.Key, a => new { value = a.Value })
            })).JsonResultAsync(ChangeResult<ResultBase>); ;
        }

        /// <summary>
        /// 解密数据
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="code"></param>
        /// <param name="encryptedData"></param>
        /// <param name="iv"></param>
        /// <returns></returns>
        public string EncryptedData(string sessionKey, string encryptedData, string iv)
        {
            return Cryptography.AESDecrypt(encryptedData, sessionKey, iv);
        }
        /// <summary>
        /// 无限个数小程序二维码
        /// </summary>
        /// <param name="scene"></param>
        /// <param name="page"></param>
        /// <returns></returns>
        public async Task<Stream> GetUnlimited(string scene, string page)
        {
            string url = "https://api.weixin.qq.com/wxa/getwxacodeunlimit";
            AppendToken(ref url);
            var respone = await httpClient.PostAsync(url, new JsonContent(new
            {
                scene,
                page
            }));
            return await respone.Content.ReadAsStreamAsync();
        }
        /// <summary>
        /// 追加Token
        /// </summary>
        /// <param name="url"></param>
        private void AppendToken(ref string url)
        {
            string accessToken = GetAccessToken().GetAwaiter().GetResult();
            url += url.Contains("?") ? "&" : "?";
            url += $"access_token={accessToken}";
        }

        /// <summary>
        /// 获取token
        /// </summary>
        /// <returns></returns>
        public Task<string> GetAccessToken()
        {
            string url = $"https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential&appid={options.AppID}&secret={options.AppSecret}";
            return memoryCache.GetOrCreateAsync($"ACCESS_TOKEN_{options.AppID}", async item =>
            {
                var token = await httpClient.GetAsync(url).JsonResultAsync(ChangeResult<ResultAccessToken>);
                item.SetAbsoluteExpiration(TimeSpan.FromSeconds(token.expires_in - 10));
                return token.access_token;
            });
        }

        public void Dispose()
        {
            httpClient.Dispose();
        }
    }
}
